//
//  ZJob.h
//  Part of Z-Way.C library
//
//  Created by Alex Skalozub on 1/9/12.
//  Based on Z-Way source code written by Christian Paetz and Poltorak Serguei
//
//  Copyright (c) 2012 Z-Wave.Me
//  All rights reserved
//  info@z-wave.me
//
//  This source file is subject to the terms and conditions of the
//  Z-Wave.Me Software License Agreement which restricts the manner
//  in which it may be used.
//

#ifndef zway_job_h
#define zway_job_h

#define MAX_PACKET_SIZE 257 // 255 + length + packet_type

#define FIXED_PAYLOAD_SIZE sizeof(void*)

#define MAX_EXPECTED_REPLY_PAYLOAD_SIZE 4 // it is enough to keep CC + Cmd + some type + sub type

enum JobPriority {
    JOB_PRIORITY_LOW,
    JOB_PRIORITY_NORMAL,
    JOB_PRIORITY_HIGH, // reserved
    JOB_PRIORITY_URGENT
};

enum JobInnerPayload {                                                // the order is defined near CC:0000.00.00.11.021
    JOB_INNER_PAYLOAD_PLAIN = 0,                                      // the packet itself
    JOB_INNER_PAYLOAD_MULTICOMMAND = JOB_INNER_PAYLOAD_PLAIN,         // MultiCmd - always a separate job
    JOB_INNER_PAYLOAD_SUPERVISION,                                    // Supervision
    JOB_INNER_PAYLOAD_MULTICHANNEL,                                   // MultiChannel
    JOB_INNER_PAYLOAD_OUTER,                                          // Outer packet
    JOB_INNER_PAYLOAD_CRC16 = JOB_INNER_PAYLOAD_OUTER,                // CRC16
    JOB_INNER_PAYLOAD_SECURITY = JOB_INNER_PAYLOAD_OUTER,             // Security - always a separate job
    JOB_INNER_PAYLOAD_TRANSPORT_SERVICE = JOB_INNER_PAYLOAD_OUTER,    // Transport Service  - always a separate job
    
    JOB_INNER_PAYLOAD_MAX_COUNT                                       // max depth of encapsulation
};

struct _ZJob 
{
    const ZFunctionClass *fc;
    ZWBYTE frame_type;
    ZWBYTE callback_id;
    ZWNODE node_id;
    ZWBYTE instance_id;
    ZWBYTE my_instance_id;
    
    ZWBOOL sent:1;
    ZWBOOL received_ack:1;
    ZWBOOL received_response:1;
    ZWBOOL received_callback:1;
    ZWBOOL received_reply:1;
    ZWBOOL done:1;
    ZWBOOL wait_wakeup:1;
    ZWBOOL wait_security:1;
    ZWBOOL await_ack:1;
    ZWBOOL await_response:1;
    ZWBOOL await_callback:1;
    ZWBOOL await_reply:1;
    ZWBOOL is_get:1;
    ZWBOOL explicit_callback:1;
    ZWBOOL sleeping_check:1;
    ZWBOOL urgent:1;
    ZWBOOL block:1;
    ZWBOOL allow_duplicate:1;
    ZWBOOL send_to_node:1;
    ZWBOOL security_following:1;
    ZWBOOL encapsulated:1;
    ZWBOOL callback_done:1;
    ZWBOOL multicmd:1;
    ZWBOOL queue_ttl:1;
    ZWBOOL is_supervision_get:1;
    ZWBOOL is_crc16:1;
    ENUMBF(JobPriority) priority:2;
    
    ZWBYTE s2_key_class;
    
    ZWBYTE send_count;   // max resend count should fit in one byte
    ZWBYTE request_send_count; // max request count should fit in one byte
    ZWBYTE payload_size;
    ZWBYTE tail_size; // size of FC tail bytes
    ZWBYTE special_position_callback; // 0 if normal mode or position of the callback id in the packet (in that case payload should have an allocated byte for it) 
    
    union {
        // using static buffer for small payloads
        ZWBYTE payload[FIXED_PAYLOAD_SIZE];
        // or allocated memory for long ones
        ZWBYTE *payload_ptr;
    };
    
    // max depth of encapsulations is 4:
    //                            3             2             1                            0
    // - [TransportService] >   CRC16    > MultiChannel > Supervision > [MultiCommand] > payload
    // - [TransportService] > [Security] > MultiChannel > Supervision > [MultiCommand] > payload
    // TransportService, Security S0/S2 and MultiCommand do not store offset/size since we never need to unpack them in the queue handling
    ZWBYTE inner_payload_offset[JOB_INNER_PAYLOAD_MAX_COUNT];
    ZWBYTE inner_payload_size[JOB_INNER_PAYLOAD_MAX_COUNT];
    
    ZWBYTE expected_reply_payload_size;
    ZWBYTE expected_reply_payload[MAX_EXPECTED_REPLY_PAYLOAD_SIZE];
    
    float timeout;
    
    ZJobList on_behalf_of;
    
    ZJobCallbackListEntry callbacks;
    
    struct timeval sent_timestamp;
    ZWSTR description;
    ZWSTR progress;
};


void _zway_job_free(ZJob job);

void _zway_job_nack(ZWay zway, ZJob job);
void _zway_job_ack(ZWay zway, ZJob job);
void _zway_job_remove(ZWay zway, ZJob job);
void _zway_job_cancel(ZWay zway, ZJob job);
void _zway_job_cancel_sent(ZWay zway, ZJob job);
void _zway_job_resend(ZWay zway, ZJob job);
void _zway_job_response(ZWay zway, ZJob job);
void _zway_job_callback(ZWay zway, ZJob job);
void _zway_job_wait_wakeup(ZWay zway, ZJob job);
void _zway_job_mark_sent(ZWay zway, ZJob job);
void _zway_job_replay(ZWay zway, ZJob job);
void _zway_job_dont_wait_reply(ZWay zway, ZJob job);
void _zway_job_reply_timeout(ZWay zway, ZJob job);
void _zway_job_on_reply(const ZWay zway, const ZJob job);
void _zway_job_on_reply_timeout(const ZWay zway, const ZJob job);
void _zway_job_on_rejected_reply(const ZWay zway, const ZJob job);
void _zway_job_delay_request_timeout(ZWay zway, ZJob job, int delay);
void _zway_job_progress(const ZWay zway, ZJob job, PRINTF_FORMAT_STRING ZWCSTR message, ...) __printflike(3, 4);
void _zway_job_on_success(const ZWay zway, const ZJob job);
void _zway_job_on_fail(const ZWay zway, const ZJob job);

ZWEXPORT_PRIVATE ZWCSTR _zway_job_get_description(const ZJob job);
ZWCSTR _zway_job_to_node(ZWNODE node_id);

ZWBOOL _zway_job_can_encapsulate(const ZJob job, const ZCommandClass *into);
ZWBOOL _zway_job_is_cc(const ZCommandClass *cc, const ZJob job);
ZWBOOL _zway_job_is_multicmd(const ZJob job, ZWNODE node_id);
ZWBOOL _zway_job_is_wakeup_no_more_info(const ZJob job, ZWNODE node_id);
ZWBOOL _zway_job_is_security_encapsulation(const ZJob job);
ZWBOOL _zway_job_is_security_s2_encapsulation(const ZJob job);
ZWBOOL _zway_job_is_security_nonce_report(const ZJob job);
ZWBOOL _zway_job_is_security_s2_nonce_report(const ZJob job);
ZWBOOL _zway_job_is_security_nonce_report_to_node(const ZJob job, ZWNODE node_id);
ZWBOOL _zway_job_is_security_nonce_get(const ZJob job);
ZWBOOL _zway_job_is_security_s2_nonce_get(const ZJob job);
ZWBOOL _zway_job_is_nooperation(const ZJob job, ZWNODE node_id);
ZWBOOL _zway_job_is_device_reset_locally(const ZJob job);
ZWBOOL _zway_job_is_transport(const ZJob job);

#endif
